package com.fdb.efp.flow.executor;

import java.util.List;
import java.util.Objects;

import com.fdb.efp.flow.common.vo.PageApproveVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.fdb.efp.flow.common.constance.FlowConstance;
import com.fdb.efp.flow.common.service.Executor;
import com.fdb.efp.flow.common.util.EngineUtil;
import com.fdb.efp.flow.common.vo.EngineVO;
import com.fdb.efp.flow.enumeration.NodeExecuteTypeBean;
import com.fdb.efp.flow.service.facade.BizFlowNodeService;
import com.fdb.efp.flow.service.facade.BizFlowProcessService;
import com.fdb.efp.flow.service.facade.BizFlowRouteService;
import com.fdb.efp.flow.service.facade.InstFlowEventService;
import com.fdb.efp.flow.service.facade.InstFlowParamService;
import com.fdb.efp.flow.service.facade.InstFlowProcessService;
import com.fdb.efp.flow.service.vo.BizFlowNodeVO;
import com.fdb.efp.flow.service.vo.BizFlowProcessVO;
import com.fdb.efp.flow.service.vo.BizFlowRouteVO;
import com.fdb.efp.flow.service.vo.InstFlowEventVO;
import com.fdb.efp.flow.service.vo.InstFlowParamVO;
import com.fdb.efp.flow.service.vo.InstFlowProcessVO;
import com.fdb.basic.framework.core.constant.FlowExceptionType;
import com.fdb.basic.framework.core.exception.FlowException;
import com.fdb.basic.framework.core.exception.TerminateException;
import com.fdb.basic.framework.core.util.SpringContextUtils;
import com.fdb.basic.framework.core.util.StringUtil;
import com.fdb.basic.framework.core.util.TimeUtil;
import com.fdb.basic.framework.core.util.UUIDUtil;


/**
 * 
 * 事件执行器
 * 
 * @author Cytus_
 * @since 2018年5月7日 下午9:16:19
 * @version 1.0
 *
 */
@Service("eventExecutor")
public class EventExecutor implements Executor {

	private static Logger logger = LoggerFactory.getLogger(EventExecutor.class);
	
	/*@Autowired
	@Qualifier("nodeExecutor")*/
	private Executor nodeExecutor;
	
	@Autowired
	@Qualifier("routeExecutor")
	private Executor routeExecutor;
	
	@Autowired
	@Qualifier("instFlowEventService")
	private InstFlowEventService instFlowEventService;
	
	@Autowired
	@Qualifier("bizFlowRouteService")
	private BizFlowRouteService bizFlowRouteService;
	
	@Autowired
	@Qualifier("instFlowProcessService")
	private InstFlowProcessService instFlowProcessService;
	
	@Autowired
	@Qualifier("bizFlowNodeService")
	private BizFlowNodeService bizFlowNodeService;
	
	@Autowired
	@Qualifier("bizFlowProcessService")
	private BizFlowProcessService bizFlowProcessService;
	
	@Autowired
	@Qualifier("instFlowParamService")
	private InstFlowParamService instFlowParamService;
	
	
	public EngineVO execute(EngineVO evo) throws FlowException {
	    
	    InstFlowEventVO eventVo = instFlowEventService.queryByBizSerno(evo.getBizSerno());
	    //判断是否实例化了事件信息，事件实例化应该在调用时已经处理，此处不再处理
	    if (Objects.isNull(eventVo)) {
	        logger.error("根据业务流水号："+ evo.getBizSerno() +"尚未找到对应的实例化事件信息!");
	        throw new FlowException("根据业务流水号："+ evo.getBizSerno() +"尚未找到对应的实例化事件信息!");
	    } else if (FlowConstance.EVENT_STATUS_END.equals(eventVo.getBizEventState()) || FlowConstance.EVENT_STATUS_TERMINATE.equals(eventVo.getBizEventState())) {
	        logger.error("事件实例："+ evo.getBizSerno() +", 当前事件已终止!");
	        throw new FlowException("事件实例："+ evo.getBizSerno() +", 当前事件已终止!");
	    }
		
		evo.setEventExecuteStatus(FlowConstance.EVENT_STATUS_RUNNING);
		logger.info("事件ID："+ evo.getEventId() +", 事件实例化ID："+ evo.getEventInstanceId() +"执行开始......");
		
		try {
			
		    if (StringUtil.isEmpty(evo.getFlowInstanceId())) {
		        evo.setFlowInstanceId(eventVo.getInstFlowId());
		    }
		    
			evo.setEventId(eventVo.getBizEventId());
			/**
			 * 判断是否已经有存在实例化的事件下节点信息
			 */
			//不存在实例化节点信息，实例化该事件下默认第一个待执行节点信息
			if (StringUtil.isEmpty(eventVo.getInstNodeId())) {
				logger.info("事件ID："+ evo.getEventId() +", 事件实例化ID："+ evo.getEventInstanceId() +"实例化第一个待执行节点信息开始......");
				//寻找该事件下默认第一个待执行节点信息
				String currNodeId = findCurrNodeId(evo.getFlowId(), eventVo.getBizEventId());
				evo.setNodeId(currNodeId);
				String instNodeId = initProcessInfo(evo);
				if (StringUtil.isEmpty(instNodeId)) {
					logger.error("根据业务流水号："+ evo.getBizSerno() +", 业务节点编号:"+ evo.getNodeId() +"初始化节点信息失败!");
					throw new FlowException("根据业务流水号："+ evo.getBizSerno() +", 业务节点编号:"+ evo.getNodeId() +"初始化节点信息失败!");
				}
				evo.setNodeInstanceId(instNodeId);
				evo.setNodeExecuteStatus(FlowConstance.NODE_STATUS_READY);
				logger.info("事件ID："+ evo.getEventId() +", 事件实例化ID："+ evo.getEventInstanceId() +"实例化第一个待执行节点信息结束......");
			} else {
				if (StringUtil.isEmpty(evo.getNodeInstanceId())) 
					evo.setNodeInstanceId(eventVo.getInstNodeId());
				//寻找当前已经实例化的节点信息
				InstFlowProcessVO node = instFlowProcessService.queryByNodeInstanceId(evo.getNodeInstanceId());
				if (Objects.isNull(node)) {
					logger.error("事件实例号："+ eventVo.getBizSerno() +"对应节点实例号:"+ eventVo.getInstNodeId() +"未在实例表中找到!!!");
					throw new FlowException("事件实例号："+ eventVo.getBizSerno() +"对应节点实例号:"+ eventVo.getInstNodeId() +"未在实例表中找到!!!");
				}
				
				evo.setNodeId(node.getBizNodeId());
				evo.setNodeExecuteStatus(node.getInstNodeState());
				logger.info("事件ID："+ evo.getEventId() +", 事件实例化ID："+ evo.getEventInstanceId() +"传到待执行节点实例信息为:"+ eventVo.getInstNodeId());
			}
		
			//更新事件状态
			updateNodeAndEventStatue(evo.getNodeInstanceId(), evo.getEventExecuteStatus(), evo.getBizSerno());
			nodeExecutor = (Executor) SpringContextUtils.getBean(NodeExecuteTypeBean.getNodeExecuteTypeBeanId(evo.getNodeExecuteType()));
			/*
			 * 循环执行当前节点下，以下情况将会退出循环
			 * 1：当前节点执行完成后状态为暂停
			 * 2：当前节点执行完成后状态为异常
			 * 3：当前节点执行完成未在路由配置表中找到下一执行节点
			 * 
			 * 退出循环后前2中情况将会将当前事件状态修改为暂停
			 */
			while (true) {
				//执行当前节点
				logger.info("事件ID："+ evo.getEventId() +", 事件实例化ID："+ evo.getEventInstanceId() +", 节点实例编号:"+ evo.getNodeInstanceId() +"调用开始......");
				evo = nodeExecutor.execute(evo);
				logger.info("事件ID："+ evo.getEventId() +", 事件实例化ID："+ evo.getEventInstanceId() +", 节点实例编号:"+ evo.getNodeInstanceId() +"调用结束......");
				//判断当前节点执行完成后状态
				if (FlowConstance.NODE_STATUS_END.equals(evo.getNodeExecuteStatus()) || FlowConstance.NODE_STATUS_TERMINATE.equals(evo.getNodeExecuteStatus())) {
					//节点状态为正常完成，调用路由执行器执行当前执行完成的节点配置的路由信息
					logger.info("事件ID："+ evo.getEventId() +", 事件实例化ID："+ evo.getEventInstanceId() +", 节点实例编号:"+ evo.getNodeInstanceId() +"执行结束, 当前节点编号为："+ evo.getNodeId() +"下一路由信息计算开始......");
					evo = routeExecutor.execute(evo);
					if (StringUtil.isNotEmpty(evo.getNodeId())) {  //存在下一路由节点
						//初始化下一路由节点
						logger.warn("事件ID："+ evo.getEventId() +", 事件实例化ID："+ evo.getEventInstanceId() +", 节点编号为："+ evo.getPreNodeId() +"路由执行结束, 路由计算结果为"+ evo.getNodeId() +", 初始化下一路由节点信息!");
						String instNodeId = initProcessInfo(evo);
						evo.setNodeInstanceId(instNodeId);
						evo.setNodeExecuteStatus(FlowConstance.NODE_STATUS_READY);
						evo.setTaskExecuteStatus(null);
						evo.setTaskId(null);
						evo.setPreTaskId(null);
						instFlowEventService.updateInstNodeIdByBizSerno(evo.getBizSerno(), instNodeId);
					} else { //不存在下一路由节点，当前事件执行结束
						evo.setEventExecuteStatus(FlowConstance.EVENT_STATUS_END);
						logger.warn("事件ID："+ evo.getEventId() +", 事件实例化ID："+ evo.getEventInstanceId() +",  节点编号为："+ evo.getPreNodeId() +"路由执行结束, 路由计算结果为无下一节点信息, 结束当前事件!");
						break;
					}
				} else if (FlowConstance.NODE_STATUS_PAUSE.equals(evo.getNodeExecuteStatus()) 
						|| FlowConstance.NODE_STATUS_EXCEPTION.equals(evo.getNodeExecuteStatus())) { //当前节点执行完成状态为暂停或异常，修改事件状态为暂停
					evo.setEventExecuteStatus(FlowConstance.EVENT_STATUS_PAUSE);
					break;
				} 
			}
			
			if (FlowConstance.EVENT_STATUS_END.equals(evo.getEventExecuteStatus())) {
				try {
					savePluginWhenEventEnd(evo);
				} catch (Exception e) {
					logger.info("事件ID："+ evo.getEventId() +", 事件实例化ID："+ evo.getEventInstanceId() +"序列化最终执行信息失败!");
				}
			}
			
		}  catch (TerminateException e) {
			logger.warn("事件ID："+ evo.getEventId() +", 事件实例化ID："+ evo.getEventInstanceId() +"执行出现终止提示, cause by:"+ e.getMessage());
			evo.setEventExecuteStatus(FlowConstance.EVENT_STATUS_TERMINATE);
            if (e.getFlowExceptionType().isHighLevel(FlowExceptionType.EVENT)) {
                throw e;
            }
		} catch (Exception e) {
			logger.info("事件ID："+ evo.getEventId() +", 事件实例化ID："+ evo.getEventInstanceId() +"执行出现异常!", e);
			evo.setEventExecuteStatus(FlowConstance.EVENT_STATUS_PAUSE);
			throw e;
		} finally {
			/**modify by chenxuan 流程打回节点更新交由流程后处理*/
			if(evo.getPluginVo().getVos().get(FlowConstance.PAGE_PLUGIN_VO_KEY2) != null){
				PageApproveVO vo = (PageApproveVO)evo.getPluginVo().getVos().get(FlowConstance.PAGE_PLUGIN_VO_KEY2);
				if(!FlowConstance.REPULSECODE.equals(vo.getAprvOpinion())){
					updateNodeAndEventStatue(evo.getNodeInstanceId(), evo.getEventExecuteStatus(), evo.getBizSerno());
				}
			}else {
				updateNodeAndEventStatue(evo.getNodeInstanceId(), evo.getEventExecuteStatus(), evo.getBizSerno());
			}
		}
		logger.info("事件ID："+ evo.getEventId() +", 事件实例化ID："+ evo.getEventInstanceId() +"执行结束......");
		return evo;
	}
	
	/**
	 * 初始化节点信息
	 * @param nodeid 节点ID
	 * @param bizSerno 实例化事件ID
	 * @param flowInstId 流程实例化ID
	 * @return 实例化节点ID
	 * @throws FlowException
	 */
	private String initProcessInfo(EngineVO evo) throws FlowException {
		BizFlowNodeVO bizFlowNode = getBizFlowNodeByNodeId(evo.getNodeId());
		if (Objects.isNull(bizFlowNode)) {
			throw new FlowException("节点ID："+ evo.getNodeId() +"未在配置中找到!");
		}
		InstFlowProcessVO node = new InstFlowProcessVO();
		node.setInstNodeId(UUIDUtil.getUUID());
		node.setBizSerno(evo.getBizSerno());
		node.setBizNodeId(evo.getNodeId());
		node.setBizNodeName(bizFlowNode.getBizNodeName());
		node.setBizStageId(bizFlowNode.getBizStageId());
		node.setBizStageName(bizFlowNode.getBizStageName());
		node.setBizNodeType(bizFlowNode.getBizNodeType());
		node.setProcessState(FlowConstance.PROCESS_STATE_WAIT);
		node.setProcessUserId(null);
		node.setProcessUserName(null);
		node.setProcessOrgId(null);
		node.setProcessOrgName(null);
		node.setStartTime(TimeUtil.getCurrentDateTime());
		node.setEndTime(null);
		node.setApproveComment(null);
		node.setApproveOpinion(null);
		node.setInstFlowId(evo.getFlowInstanceId());
		node.setInstNodeState(FlowConstance.NODE_STATUS_READY);
		int count = instFlowProcessService.insertInstFlowProcess(node);
		return count > 0 ? node.getInstNodeId() : null;
	}
	
	
	/**
	 * 更新事件状态
	 * @param currInstNodeId 当前节点实例化ID
	 * @param eventStatue 当前事件状态
	 * @param instEventId 事件实例化ID
	 * @return
	 */
	public int updateNodeAndEventStatue(String currInstNodeId, String eventStatue, String instEventId) {
		InstFlowEventVO event = new InstFlowEventVO();
		event.setInstNodeId(currInstNodeId);
		event.setBizEventState(eventStatue);
		event.setBizSerno(instEventId);
		if (FlowConstance.EVENT_STATUS_END.equals(eventStatue) || FlowConstance.EVENT_STATUS_TERMINATE.equals(eventStatue)) {
			event.setEndTime(TimeUtil.getCurrentDateTime());
		}
		return instFlowEventService.updateNodeAndEventStatue(event);
	}

	
	/**
	 * 根据事件ID从配置表中寻找当前事件待执行的节点，由路由表中配置信息查找，
	 * @param eventId 事件ID
	 * @return 待执行的节点ID
	 * @throws FlowException
	 */
	private String findCurrNodeId(String flowId, String eventId) throws FlowException {
		List<BizFlowRouteVO> routeVos = null;
		
		//调用服务查询路由信息查询路由信息
		try {
			routeVos = this.bizFlowRouteService.queryAllByEventId(flowId, eventId);
		} catch (Exception e) {
			logger.error("根据事件ID:"+eventId +"调用服务["+ bizFlowRouteService.getClass() +"]出现异常!", e);
			throw new FlowException("根据事件ID:"+eventId +"调用服务["+ bizFlowRouteService.getClass() +"]出现异常!", e);
		}
		if (Objects.isNull(routeVos) || routeVos.isEmpty()) {
			logger.warn("根据事件ID:"+ eventId +"未查询到对应的路由信息, 即将查询默认配置的第一个节点进行执行!");
			BizFlowProcessVO process = this.getDefaultProcessVO(flowId, eventId);
			if (Objects.isNull(process)) {
				logger.warn("根据事件ID:"+ eventId +", 流程ID："+ flowId +", 未查询到默认配置的第一个节点!");
				throw new FlowException("根据事件ID:"+ eventId +", 流程ID："+ flowId +", 未查询到默认配置的第一个节点!");
			}
			return process.getBizNodeId();
		}
		if (routeVos.size() == 1) {
			return routeVos.get(0).getBizSourceNodeId();
		} else {
			BizFlowRouteVO tempVo = null;
			BizFlowRouteVO currVo = routeVos.get(0);
			while (true) {
				BizFlowRouteVO temp = findSourceNodeId(routeVos, currVo);
				if (Objects.isNull(temp)) {
					tempVo = currVo;
					break;
				}else{
					tempVo = temp;
					break;
				}
			}
			return tempVo.getBizSourceNodeId();
		}
	}
	
	/**
	 * 通过上一执行节点找到下一个执行交接点
	 * @param vos
	 * @param currVo
	 * @return
	 */
	private static BizFlowRouteVO findSourceNodeId(List<BizFlowRouteVO> vos, BizFlowRouteVO currVo) {
		return vos.parallelStream().filter(s -> currVo.getBizSourceNodeId().equals(s.getBizTargetNodeId())).findFirst().orElse(null);
		
	}
	
	/**
	 * 配置node信息查找
	 * @param nodeId
	 * @return
	 */
	private BizFlowNodeVO getBizFlowNodeByNodeId(String nodeId) {
		BizFlowNodeVO queryInfo = new BizFlowNodeVO();
		queryInfo.setBizNodeId(nodeId);
		return bizFlowNodeService.queryByPk(queryInfo);
	}
	
	/**
	 * 获得默认的节点信息
	 */
	private BizFlowProcessVO getDefaultProcessVO(String flowId, String eventId) {
		BizFlowProcessVO queryInfo = new BizFlowProcessVO();
		queryInfo.setBizFlowId(flowId);
		queryInfo.setBizEventId(eventId);
		return bizFlowProcessService.queryDefaultByEventAndFlowId(queryInfo);
	}
	
	/**
	 * 任务结束保存当前上下文pluginvo
	 * @param evo
	 * @throws FlowException
	 */
	private void savePluginWhenEventEnd(EngineVO evo) throws FlowException {
		String serializeStr = null;
		try {
			serializeStr = EngineUtil.serializePluginVo(evo.getPluginVo());
		} catch (Exception e) {
			logger.error("当前节点ID:"+ evo.getNodeId() +", 实例化流水:"+ evo.getNodeInstanceId() +"序列化相关参数异常!", e);
			throw new FlowException("当前节点ID:"+ evo.getNodeId() +", 实例化流水:"+ evo.getNodeInstanceId() +"序列化相关参数异常!", e);
		}
		
		InstFlowParamVO param = new InstFlowParamVO();
		param.setBizSerno(evo.getBizSerno());
		
		//先删除在新增
		instFlowParamService.deleteByPk(param);
		param.setBizParamContent(serializeStr);
		instFlowParamService.insertInstFlowParam(param);
	}
	
}
